#include "YXLRU.h"

#include <stdio.h>
#include <stdlib.h>

static YXLRUNode *yx_lru_create_node(void *key, void *value);
static void       yx_lru_free_node(YXLRU *lru, YXLRUNode *node);
static void       yx_lru_remove_node(YXLRU *lru, YXLRUNode *node);
static void       yx_lru_push_back_node(YXLRU *lru, YXLRUNode *node);

YXLRU *yx_lru_create(int capacity, void (*keyDestroy)(void *key), void (*valueDestroy)(void *value)) {
    YXLRU *lru        = (YXLRU *) malloc(sizeof(YXLRU));
    lru->capacity     = capacity;
    lru->table        = hash_table_create();
    lru->dummy        = yx_lru_create_node(NULL, NULL);
    lru->dummy->next  = lru->dummy;
    lru->dummy->prev  = lru->dummy;
    lru->valueDestroy = valueDestroy;
    lru->keyDestroy   = keyDestroy;
    return lru;
}

void *yx_lru_get(YXLRU *lru, void *key) {
    YXLRUNode *node = hash_table_get(lru->table, key);
    if (node == NULL)
        return NULL;
    // 移动节点链表尾部, 代表最近使用过
    yx_lru_remove_node(lru, node);
    yx_lru_push_back_node(lru, node);

    return node->value;
}

void yx_lru_put(YXLRU *lru, void *key, void *value) {
    if (hash_table_have(lru->table, key)) {
        YXLRUNode *node = hash_table_get(lru->table, key);
        node->value     = value;
        yx_lru_remove_node(lru, node);
        yx_lru_push_back_node(lru, node);
        return;
    }
    if (lru->capacity == hash_table_get_size(lru->table)) {
        YXLRUNode *node = lru->dummy->next;
        // 删除链表头节点
        yx_lru_remove_node(lru, node);
        // 删除哈希表节点
        hash_table_remove(lru->table, node->key);
        // 释放节点
        yx_lru_free_node(lru, node);
    }
    YXLRUNode *node = yx_lru_create_node(key, value);
    // 插入链表尾部
    yx_lru_push_back_node(lru, node);
    // 插入哈希表
    hash_table_put(lru->table, key, node);
}

void yx_lru_print(YXLRU *lru) {
    printf("---------------------------------------------------\n");
    for (YXLRUNode *node = lru->dummy->next; node != lru->dummy; node = node->next) {
        printf("key: %p, value: %p\n", node->key, node->value);
    }
    printf("---------------------------------------------------\n");
}

static YXLRUNode *yx_lru_create_node(void *key, void *value) {
    YXLRUNode *node = (YXLRUNode *) malloc(sizeof(YXLRUNode));
    node->key       = key;
    node->value     = value;

    return node;
}

static void yx_lru_free_node(YXLRU *lru, YXLRUNode *node) {
    if (lru->keyDestroy != NULL) {
        lru->keyDestroy(node->key);
    }
    if (lru->valueDestroy != NULL) {
        lru->valueDestroy(node->value);
    }
    free(node);
}

static void yx_lru_remove_node(YXLRU *lru, YXLRUNode *node) {
    node->prev->next = node->next;
    node->next->prev = node->prev;
}

static void yx_lru_push_back_node(YXLRU *lru, YXLRUNode *node) {
    node->next             = lru->dummy;
    node->prev             = lru->dummy->prev;
    lru->dummy->prev->next = node;
    lru->dummy->prev = node;
}